package com.handinfo.android.core.net;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.net.UnknownHostException;

import com.handinfo.android.utils.Tools;

/**
 * @author Xia Mingwei
 * @version 1.0
 */
public class TcpConnection extends BaseConnection
{

	private ReaderThread m_rthread; // 读线程
	private WriterThread m_wthread; // 写线程

	private boolean m_isClosing; // 连接是否关闭

	public TcpConnection(String server, int port, IConnectionHandler recver)
	{
		m_handler = recver;
		setServiceInfo(server, port);
		m_alive = false;
	}

	/**
	 * @param server
	 * @param port
	 * @roseuid 431FC903035B
	 */
	public void setServiceInfo(String ip, int port)
	{
		m_server = ip;
		m_port = port;
		StringBuffer sname = new StringBuffer();
		// sname.append("socket://");
		sname.append(m_server);
		sname.append(":");
		sname.append(m_port);
		m_url = sname.toString();
	}

	/**
	 * 打开一个连接，并当成一个线程启动
	 */
	public void open()
	{
		if (m_alive)
			return;
		super.open();
		m_isClosing = false;

		// 打开连接
		if (openSocket()) {
			// 标志此连接是可用的
			m_alive = true;
			// 通知handler线程创建
			m_handler.recvMessage(GameProtocol.CONNECTION_CREATED, null);
			// 创建网络读写线程
			m_rthread = new ReaderThread();
			m_wthread = new WriterThread();

			new Thread(this).start();
		}
	}

	/**
	 * 关闭连接
	 */
	public void close()
	{
		m_isClosing = true;
		m_rthread = null;
		m_wthread = null;
		super.close();
	}

	/**
	 * 定时发送心跳包
	 */
	public void keepAlive()
	{
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		DataOutputStream dos = new DataOutputStream(baos);
		try {
			dos.writeLong(System.currentTimeMillis());
			sendMessage(BaseConnection.DEST_GATEWAY, 0,
					GameProtocol.CS_HEARTBEAT, baos.toByteArray());
			dos.close();
			dos = null;
			baos.close();
			baos = null;
		}
		catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 发送消息
	 * 
	 * @param destId
	 * @param type
	 * @param order
	 * @param body
	 * @return
	 */
	public boolean sendMessage(int destId, int type, int order, byte[] body)
	{
		byte[] pack = packageMessage(destId, type, order, body);
		// 把一个数据包放进发送队列，并通知发送线程
		addRequest(pack);
		return true;
	}

	/**
	 * 发送消息
	 * 
	 * @param type
	 * @param order
	 * @param body
	 * @return
	 */
	public boolean sendMessage(int type, int order, byte[] body)
	{
		int destId = DEST_WORLD;
		byte[] pack = packageMessage(destId, type, order, body);
		// 把一个数据包放进发送队列，并通知发送线程
		addRequest(pack);
		return true;
	}

	/**
	 * 线程运行
	 */
	public void run()
	{
//		android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_DISPLAY);

		while (!m_isClosing) {
			try {
				Body body = getReceive();
				if (body != null) {
					// 记录当前需要处理的消息的id
					s_perseOrderID = body.mId;
					// 处理消息体
					perseBody(body.mData);
					body.dispose();
					body = null;
//					snooze(10);
				}
			}
			catch (Exception e) {
				Tools.debug(e);
			}
		}

	}

	/**
	 * 打开socket连接
	 */
	private boolean openSocket()
	{
		try {
			// 打开一个输入输出流连接
			m_socket = new Socket(m_server, m_port);
			m_socket.setReceiveBufferSize(1024000);
			m_socket.setSendBufferSize(102400);
			m_is = new DataInputStream(m_socket.getInputStream());
			m_os = new DataOutputStream(m_socket.getOutputStream());
		}
		catch (UnknownHostException e) {
			close();
			Tools.debug(e);
//			m_handler.recvMessage(GameProtocol.CONNECTION_CREATE_FAILED, null);
			return false;
		}
		catch (IOException e) {
			close();
			Tools.debug(e);
//			m_handler.recvMessage(GameProtocol.CONNECTION_CREATE_FAILED, null);
			return false;
		}
		catch (Exception e) {
			close();
			Tools.debug(e);
//			m_handler.recvMessage(GameProtocol.CONNECTION_CREATE_FAILED, null);
			return false;
		}
		return true;
	}

	/**
	 * 读
	 */
	private void reading()
	{
		while (m_alive) {
			if (!canRead()) {
				snooze(10);
				continue;
			}
			issueRead();
		}
		if (!m_isClosing) {
			m_handler.recvMessage(GameProtocol.CONNECTION_BROKEN, null);
		}
	}

	/**
	 * 写
	 * 
	 * @throws InterruptedException
	 */
	private void sending() throws InterruptedException
	{
		while (m_alive) {
			byte[] msg = getRequest();
			if (msg != null) {
				issueWrite(msg);
				msg = null;
//				snooze(10);
			}
		}
		if (!m_isClosing) {
			m_handler.recvMessage(GameProtocol.CONNECTION_BROKEN, null);
		}
	}

	/**
	 * 处理读异常
	 * 
	 * @param e
	 */
	private void stopReadForException(Exception e)
	{
		m_alive = false;
	}

	private boolean canRead()
	{
		try {
			int num = m_is.available();
			if (num <= 0) {
				return false;
			}
			return true;
		}
		catch (IOException e) {
			Tools.debug(e);
			stopReadForException(e);
			return false;
		}
	}

	private boolean canSend()
	{
		return true;
	}

	private void issueRead()
	{
		try {
			// 读出包头数据
			byte[] header = readInputStream(m_is, PACKAGE_RECEIVE_HEADER_LENGTH);
			// 解析包头
			if (!unpackageMessage(header)) {
				throw new Exception();
			}
			// 读出包体数据
			byte[] body = readInputStream(m_is, s_bodyLength);
			// 将包体放到数据缓冲区
			addReceive(new Body(s_recvOrderID, body));
			
			body = null;
			header = null;
		}
		catch (Exception e) {
			Tools.debug(e);
			if (!m_isClosing) {
				stopReadForException(e);
			}
		}
	}

	private byte[] readInputStream(InputStream is, int bodyLength)
			throws IOException
	{
		int len = 0;// 已读取长度
		int curLen = 0;// 此次读取长度
		byte[] body = new byte[bodyLength];
		do {
			curLen = is.read(body, len, bodyLength - len);
			len += curLen;
		}
		while (len < bodyLength);
		return body;
	}
	
	private void stopSendForException(Exception e) {
		m_alive = false;
	}

	private void issueWrite(byte[] msg)
	{
		try {
			if (msg != null) {
				m_os.write(msg);
				m_os.flush();
			}
		}
		catch (Exception e) {
			e.printStackTrace();
			stopSendForException(e);
		}
	}

	private void snooze(int ms)
	{
		try {
			if (ms > 0) {
				Thread.sleep(ms);
			}
//			Thread.yield();
		}
		catch (InterruptedException ex) {
			ex.printStackTrace();
		}
	}

	private class ReaderThread implements Runnable
	{
		ReaderThread()
		{
			new Thread(this).start();
		}

		public void run()
		{
			try {
				reading();
			}
			catch (Exception ex) {
				Tools.debug(ex);
			}
		}
	}

	private class WriterThread implements Runnable
	{
		WriterThread()
		{
			new Thread(this).start();
		}

		public void run()
		{
			try {
				sending();
			}
			catch (Exception ex) {
				Tools.debug(ex);
			}
		}
	}
}
